package top.system.dept.service;

import org.apache.ibatis.jdbc.Null;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import top.system.dept.mapper.DeptMapper;
import top.system.dept.mapper.UserMapper;
import top.system.dept.pojo.Dept;
import top.system.dept.pojo.SysResp;
import top.system.dept.pojo.User;
import top.system.dept.utils.*;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * @ClassName: UserService
 * @Author: Amosen
 * @Since: 2021/5/16
 */

@Service
@Transactional(rollbackFor = {Exception.class})
public class UserService {

    public static Map<String, String> loginUser = new ConcurrentHashMap<>();

    Logger logger = LoggerFactory.getLogger(UserService.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private DeptMapper deptMapper;

    @Value("${user.root}")
    private Integer rootAuth;

    @Value("${user.manager}")
    private Integer managerAuth;

    @Value("${user.deptManager}")
    private Integer deptManageAuth;

    @Value("${mini.code2session.url}")
    private String code2session;

    @Value("${mini.appid}")
    private String appid;

    @Value("${mini.secret}")
    private String secret;

    @Value("${mini.code2session.grant_type}")
    private String grant_type;

    @Transactional(rollbackFor = {Exception.class})
    public SysResp login(String account, String password, String code) {
        if (!StringUtil.checkStrArgs(account, password, code)) {
            return RespUtil.illegalParam();
        }

        // 通过账户向数据库查询用户信息
        logger.debug("登录查询用户数据");
        User user = userMapper.getUserByAccount(account);
        if (user == null) {
            return RespUtil.fail();
        } else {
            String savedPassword = user.getPassword();
            if (!savedPassword.equals(password)) {
                return RespUtil.fail();
            } else {
                // 用户名和密码校验通过，进行后续逻辑
                // 数据库校验，主要是校验数据库中的用户是否存在用户的openid信息
                if (StringUtils.hasText(code)) {
                    doCode2Session(user, code);
                }
                // 生成用户唯一标识，采用id + account的MD5 32位加密方式生成
                String modal = SecurityUtil.encrypt32ForMD5(user.getId() + user.getAccount());

                // 将用户登录态信息保存到redis数据库，登录过期时限在常量池中
                try {
                    ValueOperations<String, String> ops = redisTemplate.opsForValue();
                    ops.set(modal, String.valueOf(user.getId()), ConstantPool.LOGIN_TIMEOUT, ConstantPool.LOGIN_UNIT); // 以用户唯一标识为key，以用户id为value存储，方便后续获得用户数据
                } catch (Exception ex) { // redis服务不可用
                    // 保存到本地
                    loginUser.put(modal, String.valueOf(user.getId()));
                }

                // 返回登录信息，包括用户唯一标识，权限信息，描述信息
                SysResp resp = RespUtil.success();
                resp.getData().put("user", modal);
                resp.getData().put("auth", user.getAuth());
                resp.getData().put("desc", user.getDescription());
                // 返回用户是否需要绑定邮箱，以便后续下发通知
                resp.getData().put("hasMail", StringUtil.strCanUse(user.getMail()));
                return resp;
            }
        }
    }

    private void doCode2Session(User user, String code) {
        Thread code2SessionThread = new Thread(
                new Code2SessionService(code2session, appid, secret, code, grant_type, user));
        code2SessionThread.setName(user.getId() + "-code2session");
        code2SessionThread.start();
    }

    public SysResp handleLogout(String userModal) {
        // 这个业务逻辑主要是在后台使用，所以应该检验当前用户是否有权限进入后台
        if (!canUseRoot(userModal)) {
            return RespUtil.fail();
        } else {
            // 从redis数据库中退出登录即可，问题在于：Redis数据库存储的是微信小程序和电脑端两端的登录用户
            // 如果删除电脑端的用户，会导致微信小程序端的强制下线，如果更改，需要更改整个会话存储技术
            // 这里暂时先不更改，因为微信小程序的请求不同于普通的网页技术，比较类似与Vue的ajax请求
            // 两次请求并不属于同一个会话，需要用数据库来保存用户的登录态，当然可以使用token来实现服务端的无状态
            // 但是这个项目里面的userModal就类似于token，考虑到获取用户数据是一个比较频繁的操作，所以使用redis数据库来存储

            Boolean delete = redisTemplate.delete(userModal);
            return delete ? RespUtil.success() : RespUtil.fail();
        }
    }


    public boolean canUseManager(String userModal) {
        User user = getUserByModal(userModal);
        if (user == null) {
            return false;
        } else {
            int auth = user.getAuth();
            return auth >= 0 && auth < managerAuth;
        }
    }

    public boolean canUseRoot(String userModal) {
        User user = getUserByModal(userModal);
        if (user == null) {
            return false;
        } else {
            int auth = user.getAuth();
            return auth >= 0 && auth <= rootAuth;
        }
    }

    // 获取root权限的账户
    public List<User> getRootUser() {
        List<User> users = userMapper.getUserByAuth(rootAuth);
        return users == null ? new ArrayList<>() : users;
    }

    // 获取Manager权限的账户
    public List<User> getManageUser() {
        return userMapper.getUserByAuth(managerAuth);
    }

    // 根据用户的唯一标识获取用户的信息(从redis中)
    public User getUserByModal(String userModal) {

        if (userModal == null) {
            return null;
        }

        String id;

        try {
            ValueOperations<String, String> ops = redisTemplate.opsForValue();
            id = ops.get(userModal);
        } catch (Exception e) {
            // redis服务不可用
            id = loginUser.get(userModal);
        }

        if (id == null) {
            return null;
        } else {
            try {
                Integer identify = Integer.parseInt(id);
                // 从数据库中获取用户信息
                User user = userMapper.getUserById(identify);
                return user;
            } catch (NumberFormatException e) {
                logger.error("从redis中取出了异常数据", e);
                return null;
            }
        }
    }

    // 根据用户id获取用户信息
    public User getUserById(Integer userId) {
        return userMapper.getUserById(userId);
    }

    public SysResp addUser(String userModal, User user, Integer deptId) {

        if (!canUseRoot(userModal)) {
            return RespUtil.fail();
        }

        User savedUser = userMapper.getUserByAccount(user.getAccount());
        if (savedUser != null) {
            return RespUtil.accountConflict();
        }

        Dept dept = deptMapper.getDeptById(deptId);
        user.setDept(dept);
        String encryptPass = SecurityUtil.encrypt32ForMD5(user.getAccount());
        user.setPassword(encryptPass);

        userMapper.addUser(user);
        return RespUtil.success();
    }

    @Transactional
    public SysResp updateUser(String userModal, User user) {
        User loginUser = getUserByModal(userModal);
        User savedUser = userMapper.getUserById(user.getId());
        savedUser.setDescription(user.getDescription());
        savedUser.setAuth(user.getAuth());
        savedUser.getDept().setId(user.getDept().getId());
        if (loginUser == null) {
            return RespUtil.fail();
        } else {
            if (!loginUser.getAccount().equals(user.getAccount())) { // 当前登录的用户和要更新的用户不是同一人
                if (loginUser.getAuth() <= rootAuth) { // 当前登录用户是管理员
                    int i = userMapper.updateUser(savedUser);
                    return i > 0 ? RespUtil.success() : RespUtil.fail();
                }
                return RespUtil.fail();
            } else {
                if (loginUser.getAuth() <= rootAuth) {
                    int i = userMapper.updateUser(savedUser);
                    return i > 0 ? RespUtil.success() : RespUtil.fail();
                } else {
                    return RespUtil.fail();
                }
            }
        }
    }

    public SysResp updateMail(String userModal, String mail) {
        User user = getUserByModal(userModal);
        if (user == null) { // 未登录
            return RespUtil.fail();
        } else {
            // 检查邮箱格式
            String mailPattern = "^([A-Za-z0-9_\\-\\.])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,4})$";
            boolean isMatches = Pattern.matches(mailPattern, mail);
            if (isMatches) {
                // 更新数据库
                user.setMail(mail);
                userMapper.updateUser(user);
                return RespUtil.success();
            } else {
                return RespUtil.fail();
            }
        }
    }

    public SysResp getAuthDetail() {
        SysResp success = RespUtil.success();
        Map<String, Object> data = success.getData();
        data.put("root", rootAuth);
        data.put("管理员", managerAuth);
        data.put("部门管理员", deptManageAuth);
        return success;
    }

    public SysResp getMembers(String userModal) {
        // 检查用户身份
        if (!canUseRoot(userModal)) {
            return RespUtil.fail();
        } else {
            List<User> users = userMapper.getAllUsers(null, null);
            return RespUtil.successData("users", users);
        }
    }

    public SysResp managerLogin(String account, String password) {
        User user = userMapper.getUserByAccount(account);
        if (user == null) {
            return RespUtil.fail();
        } else {
            String savedPass = user.getPassword();
            if (!savedPass.equals(password)) {
                return RespUtil.fail();
            }

            if (user.getAuth() < 0 || user.getAuth() > rootAuth) {
                return RespUtil.lowAuth();
            }

            String modal = SecurityUtil.encrypt32ForMD5(user.getId() + user.getAccount());

            // 将用户登录态信息保存到redis数据库，登录过期时限在常量池中
            try {
                ValueOperations<String, String> ops = redisTemplate.opsForValue();
                ops.set(modal, String.valueOf(user.getId()), ConstantPool.LOGIN_TIMEOUT, ConstantPool.LOGIN_UNIT); // 以用户唯一标识为key，以用户id为value存储，方便后续获得用户数据
            } catch (Exception ex) { // redis服务不可用
                // 保存到本地
                loginUser.put(modal, String.valueOf(user.getId()));
            }
            return RespUtil.successData("userModal", modal);
        }
    }

    public SysResp getAllUser(Integer pageNum, Integer pageSize, String userModal) {
        if (!this.canUseRoot(userModal)) {
            return RespUtil.lowAuth();
        }
        List<User> allUsers = userMapper.getAllUsers(pageNum, pageSize);
        return  RespUtil.successData("users", allUsers);
    }

    @Transactional
    public SysResp deleteUser(String userModal, Integer id) {
        if (!canUseRoot(userModal)) {
            return RespUtil.lowAuth();
        }
        int i = userMapper.deleteUserById(id);
        return i > 0 ? RespUtil.success() : RespUtil.fail();
    }

    @Transactional
    public SysResp updatePassword(String userModal, String newPass, String oldPass) {
        User user = getUserByModal(userModal);
        if (user == null) {
            return RespUtil.fail();
        } else {
            if (!oldPass.equals(user.getPassword())) {
                return RespUtil.fail();
            }
            user.setPassword(newPass);
            int i = userMapper.updateUser(user);
            return i > 0 ? RespUtil.success() : RespUtil.fail();
        }
    }

    public SysResp getCanTrans(String userModal) {
        if (!canUseRoot(userModal)) {
            return RespUtil.fail();
        } else {
            // 数据库查询可转接列表，权限要求为部门管理员
            List<User> users = userMapper.getUserByAuth(deptManageAuth);
            return RespUtil.successData("users", users);
        }
    }

    @Transactional
    public SysResp doTrans(String userModal, User toUser) {
        if (toUser == null) {
            return RespUtil.fail();
        }
        if (!canUseRoot(userModal)) { // 权限不足
            return RespUtil.fail();
        } else {
            User loginUser = getUserByModal(userModal); // 当前登录用户
            if (loginUser.getId().equals(toUser.getId())) { // 自己给自己转接
                return RespUtil.fail();
            } else {
                // 下放当前登录用户权限等级
                loginUser.setAuth(deptManageAuth);
                // 提升权限等级
                toUser.setAuth(rootAuth);
                // 更新数据库
                int i = userMapper.updateUser(loginUser);
                int j = userMapper.updateUser(toUser);
                return i > 0 && j > 0 ? RespUtil.success() : RespUtil.fail();
            }
        }
    }

    // 异步方式获取openid并存入数据库
    class Code2SessionService implements Runnable {

        private String code2session;

        private String appid;

        private String secret;

        private String code;

        private String grant_type;

        // 用户id
        private User user;

        public Code2SessionService(String code2session, String appid, String secret, String code, String grant_type, User user) {
            this.code2session = code2session;
            this.appid = appid;
            this.secret = secret;
            this.code = code;
            this.grant_type = grant_type;
            this.user = user;
        }

        // 核心方法，从微信服务器获取openid
        @Override
        public void run() {
            if (user == null) {
                return;
            }
            // 封装请求参数
            Map<String, String> params = new HashMap<>();
            params.put("appid", appid);
            params.put("secret", secret);
            params.put("js_code", code);
            params.put("grant_type", grant_type);

            // 发起请求
            Map<String, Object> result = NetWork.service(code2session,
                    HttpMethod.GET,
                    params,
                    null);

            try {
                String openid = String.valueOf(result.get("openid"));
                // 防御性编程
                if ("null".equals(openid) || null == openid) {
                    throw new NullPointerException();
                } else {
                    String savedOpenid = user.getOpenid();
                    if (savedOpenid == null) {
                        user.setOpenid(openid);
                        synchronized (this) { // 序列化方式更新数据库，保证信息安全
                            userMapper.updateUser(user);
                        }
                        savedOpenid = openid;
                    }
                    if (!savedOpenid.equals(openid)) {
                        user.setOpenid(openid);
                        synchronized (this) { // 序列化方式更新数据库，保证信息安全
                            userMapper.updateUser(user);
                        }
                    }
                }
            } catch (NullPointerException e) {
                logger.warn("微信服务器返回了异常数据", e);
            }
//            try {
//                // 获取微信服务器的响应码
//                Integer errcode = Integer.parseInt(String.valueOf(result.get("errcode")));
//                // 这里微信服务器并没有按照官方文档返回错误码，请求成功后直接返回了openid，所以直接获取openid
//
//                if (errcode == 0) { // 请求成功
//                    // 尝试获取openid
//                    try {
//                        String openid = String.valueOf(result.get("openid"));
//                        if ("null".equalsIgnoreCase(openid) || openid == null) {
//                            throw new NullPointerException("微信服务器未返回openid");
//                        } else { // openid获取成功，更新数据库
//                            User user = userMapper.getUserById(userId);
//                            if (user == null) {
//                                return;
//                            }
//                            user.setOpenid(openid);
//                            synchronized (this) { // 序列化方式更新数据库，保证信息安全
//                                userMapper.updateUser(user);
//                            }
//                        }
//
//                    } catch (NullPointerException e) {
//                        logger.warn("微信服务器未返回openid");
//                    }
//                }
//            } catch (NumberFormatException e) {
//                logger.warn("微信服务器返回了异常数据！");
//            }
        }
    }
}



